#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>
#include "container.h"

void container_initialize(int type, struct container *con)
{
	assert(con != NULL);
	con->type = type;
	con->len = CONT_INIT_LEN;
	con->size = 0;
	con->content = malloc(CONT_INIT_LEN);
}

int container_position(struct container *con, int position)
{
	if (position == -2)
		return con->size - 1;   /* Bottom */
	
	if (position == 1)
		return rand() % con->size;      /* Random */
	
	if (position < con->size)
		return position;
	
	assert(false);
}

void container_finalize(struct container *con)
{
	int size;

	assert(con != NULL);

	size = con->size;

	for(int i=0; i < size; i++) {
		free(container_remove(con, 0));
	}

	con->len = 0;
	free(con->content);
}

void container_check_len(struct container *con, int newlen)
{
	if (newlen <= con->len)
		return;
	
	con->len = (newlen * 2);
	con->content = realloc(con->content, con->len);
	T_ERROR_MALLOC(con->content);
}

void container_fill(struct container *con)
{
	struct type *t = &type_table[con->type];
	assert(t->cate == ENUM_BASIC || t->cate == ENUM_COMBO);
	if (t->cate == ENUM_BASIC) {
		struct enum_basic *enum_b = &enum_basic_table[t->spec.enum_basic.enumindex];
		container_check_len(con, con->size + enum_b->count);
		int i;
		
		for (i = 0; i < enum_b->count; i++) {
			int *value = malloc(sizeof(int));
			T_ERROR_MALLOC(value);
			*value = i;
			con->content[i] = value;
			con->size ++;
		}
	} else if (t->cate == ENUM_COMBO) {
		struct enum_combo *enum_c = &enum_combo_table[t->spec.enum_combo.enumindex];
		container_check_len(con, con->size + enum_c->count);
		int i, j, k;
				    
		for (i = 0; i < enum_c->count; i++) {
			int *value = (int *)malloc(sizeof(int) * enum_c->compo_count);
			T_ERROR_MALLOC(value);
			k = i;
			for (j = enum_c->compo_count - 1; j >= 0; j--) {
				value[j] = k % enum_basic_table[enum_c->components[j]].count;
				k /=  enum_basic_table[enum_c->components[j]].count;
			}
			con->content[i] = value;
			con->size ++;
		}
	} else {
		assert(false);
	}
}

void container_shuffle(struct container *con)
{
	int i, j;
	void *tmp;

	for (i = 0; i < con->size; i++) {
		j = rand() % con->size;
		if (i == j)
			continue;

		tmp = con->content[i];
		con->content[i] = con->content[j];
		con->content[j] = tmp;
	}
}

int container_size(struct container *con)
{
	assert(con != NULL);

	return con->size;
}

void* container_peek(struct container *con, int position)
{
	position = container_position(con, position);
	return con->content[position];
}

void* container_remove(struct container *con, int position)
{
	void *tmp;

	assert(con != NULL);

	tmp = container_peek(con, position);

	position = container_position(con, position);
	container_move_left(con, position);

	con->size--;
	return tmp;
}

void container_insert(struct container *con, int position, void *elem)
{
	int size;
	void *tmp;

	assert(con != NULL && elem != NULL);

	position = container_position(con, position);

	size = validate_type_size(con->type);
	tmp = (void *)malloc(size);
	T_ERROR_MALLOC(tmp);
		
	memcpy(tmp, elem, size);
	container_move_right(con, position);
	con->content[position] = tmp;
}

void container_move_left(struct container *con, int index)
{
	int i;

	assert(con != NULL && index < con->size && index > -1);

	for (i = index; i < con->size - 1; i++) {
		con->content[i] = con->content[i + 1];
	}

	con->size--;
}

void container_move_right(struct container *con, int index)
{
	int i;

	assert(con != NULL && index < con->size && index > -1);

	container_check_len(con, con->size + 1);

	for (i = con->size - 1; i > index; i--) {
		con->content[i] = con->content[i-1];
	}

	con->size++;
}

void container_print(struct container * con)
{
	int i;

	for (i = 0; i < con->size; i++) {
		printf("[%d] :%d", i, *(int *)container_peek(con, i));
	}
}

